package com.xg.frame.idempotent.aop;

import com.xg.frame.constant.Constant;
import com.xg.frame.entity.R;
import com.xg.frame.idempotent.annotation.ApiIdempotent;
import com.xg.frame.utils.IdempotentKeyUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 幂等校验
 *
 * @author dehuisun
 */
@Slf4j
@Aspect
@Order(-1) // 保证该AOP在@Transactional之前执行
public class IdempAnnotationAOP {

    private static final String KEY_TEMPLATE = "idempotent_%S";

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Around("@annotation(annotation)")
    public Object Idempempot(ProceedingJoinPoint jPoint, ApiIdempotent annotation) throws Throwable {
        String key = null;
        try {
            Method method = ((MethodSignature) jPoint.getSignature()).getMethod();
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            key = String.format(KEY_TEMPLATE, IdempotentKeyUtil.generate(method, jPoint.getArgs(), request.getHeader("token")));
            
            //已存在
            if (redisTemplate.hasKey(key)) {
                return R.error().code(202).message("幂等校验失败");
            }
            redisTemplate.opsForValue().set(key, key, Constant.IDEMPOTENT_TIME, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("幂等校验异常",e);
            return R.error().code(202).message("幂等异常");
        }

        try {
            return jPoint.proceed();
        } catch (Exception e) {
            // 业务代码异常抛出，交由统一异常处理
            throw e;
        } finally {
            if (redisTemplate.hasKey(key)) {
                redisTemplate.delete(key);
            }
        }
    }
}
